﻿using System;
using System.Collections;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace AMPLibrary.Collections
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

    [Serializable]
    public class Collection<Item> : IEnumerable<Item>, ISerializable
    {     
        private List<Item> items = new List<Item>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromCollection"></param>
        public Collection(IQueryable<Item> newCollection)
        {
            foreach (Item item in newCollection)
                this.AddItem(item);

            _ItemsAdded(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromCollection"></param>
        public Collection(IEnumerable<Item> newCollection)
        {
            foreach (Item item in newCollection)
                this.AddItem(item);

            _ItemsAdded(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromCollection"></param>
        public Collection(IOrderedQueryable<Item> newCollection)
        {
            foreach (Item item in newCollection)
                this.AddItem(item);

            _ItemsAdded(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromCollection"></param>
        public Collection(IOrderedEnumerable<Item> newCollection)
        {
            foreach (Item item in newCollection)
                this.AddItem(item);

            _ItemsAdded(EventArgs.Empty);
        }

        public Collection()
        {

        }

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler ItemAdded;

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler ItemRemoved;

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler ItemsAdded;

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler ItemsRemoved;

        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler ItemsChanged;

        // Invoke the Changed event; called whenever list changes
        protected virtual void _ItemAdded(EventArgs e)
        {
            if (ItemAdded != null)
                ItemAdded(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void _ItemRemoved(EventArgs e)
        {
            if (ItemRemoved != null)
                ItemRemoved(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void _ItemsAdded(EventArgs e)
        {
            if (ItemsAdded != null)
                ItemsAdded(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void _ItemsRemoved(EventArgs e)
        {
            if (ItemsRemoved != null)
                ItemsRemoved(this, e);
        }

        // Invoke the Changed event; called whenever list changes
        protected virtual void _ItemsChanged(EventArgs e)
        {
            if (ItemsChanged != null)
                ItemsChanged(this, e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        public Item GetItem(int pos)
        {
            return items[pos];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public void AddItem(Item item)
        {
            items.Add(item);

            _ItemAdded(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public void AddItems(Collection<Item> itemsToAdd)
        {
            foreach (Item item in itemsToAdd)
                this.AddItem(item);

            _ItemAdded(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public void InsertItem(int index, Item item)
        {
            if (index < 0)
                return;

            items.Insert(index, item);

            _ItemAdded(EventArgs.Empty);
            _ItemsChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public int IndexOf(Item item)
        {
            return items.IndexOf(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public void RemoveItem(Item item)
        {
            items.Remove(item);

            _ItemRemoved(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        public void RemoveItemAt(int index)
        {
            items.RemoveAt(index);

            _ItemRemoved(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        public void ReverseItems()
        {
            items.Reverse();

            _ItemsChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        public void RandomizeItems()
        {
            try
            {
                Collection<Item> collection = new Collection<Item>();

                if (this.items.Count == 0)
                    return;

                Random r = new Random();

                int randomIndex = 0;
                while (items.Count > 0)
                {
                    randomIndex = r.Next(0, items.Count); //Choose a random object in the list
                    collection.AddItem(items[randomIndex]); //add it to the new, random list
                    items.RemoveAt(randomIndex); //remove to avoid duplicates
                }

                items.Clear();

                foreach (Item media in collection)
                    items.Add(media);
            }
            catch
            {
            }

            _ItemsChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        public void ClearItems()
        {
            items.Clear();

            _ItemsChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="comparer"></param>
        public void Sort(IComparer<Item> comparer)
        {
            items.Sort(comparer);

            _ItemsChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        public int Count
        {
            get
            {
                return items.Count;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator<Item> IEnumerable<Item>.GetEnumerator()
        {
            return items.GetEnumerator();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return items.GetEnumerator();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("collection_items", this.items);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return items.GetEnumerator();
        }
    }
}